using PioneerBoilerplate.Model.Admin;
using PioneerBoilerplate.Repository;
using PioneerBoilerplate.Service.Admin.User.Input;
using PioneerBoilerplate.Service.Admin.User.Output;
using PioneerBoilerplate.Service.Base;
using PioneerBoilerplate.Util;
using PioneerBoilerplate.Util.Helpers;
using PioneerBoilerplate.Util.Output;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using PioneerBoilerplate.Service.Admin.Auth.Output;
using PioneerBoilerplate.Util.Cache;
using PioneerBoilerplate.Repository.UnitOfWorks;
using PioneerBoilerplate.Util.Input;
using Yitter.IdGenerator;
using PioneerBoilerplate.Repository.Repositories;

namespace PioneerBoilerplate.Service.Admin.User
{
    /// <summary>
    /// 用户服务
    /// </summary>
    public class UserService : BaseService, IUserService
    {
        private readonly IUserRepository _userRepository;
        private readonly IRoleRepository _roleRepository;
        private readonly IBaseRepository<ApplicationDbContext, UserRoleEntity> _userRoleBaseRepository;
        private readonly IBaseRepository<ApplicationDbContext, RolePermissionEntity> _rolePermissionBaseRepository;
        private readonly IBaseRepository<ApplicationDbContext, PermissionEntity> _permissionBaseRepository;
        private readonly IUnitOfWorkManager _unitOfWorkManager;
        public UserService(
            IUserRepository userRepository,
            IRoleRepository roleRepository,
            IBaseRepository<ApplicationDbContext, UserRoleEntity> userRoleBaseRepository,
            IBaseRepository<ApplicationDbContext, RolePermissionEntity> rolePermissionBaseRepository,
            IBaseRepository<ApplicationDbContext, PermissionEntity> permissionBaseRepository,
            IUnitOfWorkManager unitOfWorkManager)
        {
            _userRepository = userRepository;
            _roleRepository = roleRepository;
            _userRoleBaseRepository = userRoleBaseRepository;
            _rolePermissionBaseRepository = rolePermissionBaseRepository;
            _permissionBaseRepository = permissionBaseRepository;
            _unitOfWorkManager = unitOfWorkManager;
        }

        public async Task<IResponseOutput> GetAsync(long id)
        {
            var entity = await _userRepository.GetAll()
                .Where(x => x.Id == id)
                .Include(ur => ur.UserRoles)
                   .ThenInclude(r => r.Role)
                .FirstOrDefaultAsync();

            var entityDto = Mapper.Map<UserGetOutput>(entity);

            var roles = await _roleRepository.GetAll()
                .Select(a => new { a.Id, a.Name })
                .ToListAsync(); 

            return ResponseOutput.Ok(new { Form = entityDto, Select = new { roles } });
        }

        public async Task<ResponseOutput<AuthLoginOutput>> GetLoginUserAsync(long id)
        {
            var output = new ResponseOutput<AuthLoginOutput>();
            var entityDto = Mapper.Map<AuthLoginOutput>((await _userRepository.FirstOrDefaultAsync(a => a.Id == id)));

            return output.Ok(entityDto);
        }

        public async Task<IResponseOutput> PageAsync(PageInput<UserEntity> input)
        {
            PaginationSpecification<UserEntity> specification = new();
            specification.OrderBy = q => q.OrderBy(e => e.Id);
            specification.Includes = q => q.Include(e => e.UserRoles)
                                           .ThenInclude(e => e.Role);
            specification.PageIndex = input.CurrentPage;
            specification.PageSize = input.PageSize;

            var list = await _userRepository.GetListAsync(specification);
            var data = new PageOutput<UserListOutput>()
            {
                List = Mapper.Map<List<UserListOutput>>(list.Items),
                Total = list.TotalItems
            };

            return ResponseOutput.Ok(data);
        }

        public async Task<IResponseOutput> AddAsync(UserAddInput input)
        {
            if (input.Password.IsNull())
            {
                input.Password = "111111";
            }

            input.Password = MD5Encrypt.Encrypt32(input.Password);

            var entity = Mapper.Map<UserEntity>(input);
            var user = await _userRepository.InsertAsync(entity);

            if (!(user?.Id > 0))
            {
                return ResponseOutput.NotOk();
            }

            if (input.RoleIds != null && input.RoleIds.Any())
            {
                var roles = input.RoleIds.Select(a => new UserRoleEntity { UserId = user.Id, RoleId = a }).ToArray();
                await _userRoleBaseRepository.InsertsAsync(roles);
            }

            return ResponseOutput.Ok();
        }

        public async Task<IResponseOutput> BatchSoftDeleteAsync(long[] ids)
        {
            var entitys = await _userRepository.GetAllListAsync(a => ids.Contains(a.Id));
            if (entitys == null || entitys.Count == 0)
                return ResponseOutput.Result(false);

            entitys.ForEach(a => { 
                a.IsDeleted = true;
                _userRepository.UpdateAsync(a);
            });

            return ResponseOutput.Result(true);
        }

        public async Task<IResponseOutput> ChangePasswordAsync(UserChangePasswordInput input)
        {
            if (input.ConfirmPassword != input.NewPassword)
            {
                return ResponseOutput.NotOk("新密码和确认密码不一致！");
            }

            var entity = await _userRepository.FirstOrDefaultAsync(a=>a.Id== input.Id);
            var oldPassword = MD5Encrypt.Encrypt32(input.OldPassword);
            if (oldPassword != entity.Password)
            {
                return ResponseOutput.NotOk("旧密码不正确！");
            }

            input.Password = MD5Encrypt.Encrypt32(input.NewPassword);

            entity = Mapper.Map(input, entity);
            var result = (await _userRepository.UpdateAsync(entity)) != null;

            return ResponseOutput.Result(result);
        }

        public async Task<IResponseOutput> DeleteAsync(long id)
        {
            var result = true;
            if (id > 0)
            {
                var entity =await _userRepository.FirstOrDefaultAsync(a => a.Id == id);
                await _userRepository.DeleteAsync(entity);
            }

            return ResponseOutput.Result(result);
        }


        public async Task<IResponseOutput> GetBasicAsync()
        {
            if (!(User?.Id > 0))
            {
                return ResponseOutput.NotOk("未登录！");
            }

            var data = Mapper.Map<UserUpdateBasicInput>(await _userRepository.FirstOrDefaultAsync(a => a.Id == User.Id));
            return ResponseOutput.Ok(data);
        }

        public async Task<IList<UserPermissionsOutput>> GetPermissionsAsync()
        {
            var key = string.Format(CacheKey.UserPermissions, User.Id);
            var result = await Cache.GetOrSetAsync(key, async () =>
            {
                //用户角色
                var roles = (await _userRoleBaseRepository.GetAllListAsync(a => a.UserId == User.Id)).Select(a => a.RoleId).ToList();

                var permissions = (await _rolePermissionBaseRepository.GetAllListAsync(a => roles.Contains(a.RoleId))).Select(a => a.PermissionId).ToList();

               var permissionApis = await _permissionBaseRepository.GetAll()
               .Where(x => permissions.Contains(x.Id))
               .Include(p => p.PermissionApis)
               .ThenInclude(api => api.Api)
               .ToListAsync();

                List<UserPermissionsOutput> userPermissionsOutputs = new();

                permissionApis.ForEach(a =>
                {
                    a.PermissionApis.ForEach(b =>
                    {
                        userPermissionsOutputs.Add(new UserPermissionsOutput { HttpMethods = b.Api.HttpMethods, Path = b.Api.Path });
                    });
                });

                return await Task.Run(() => 
                {
                   return userPermissionsOutputs;
                });
            });
            return result;
        }

        public async Task<IResponseOutput> GetSelectAsync()
        {
            var roles = (await _roleRepository.GetAllListAsync()).Select(a => new { a.Id, a.Name });

            return ResponseOutput.Ok(new { Select = new { roles } });
        }

        public async Task<IResponseOutput> SoftDeleteAsync(long id)
        {
            var user = await _userRepository.FirstOrDefaultAsync(a => a.Id == id);
            if (!(User?.Id > 0))
            {
                return ResponseOutput.NotOk("用户不存在！");
            }
            user.IsDeleted = true;
            var result = (await _userRepository.UpdateAsync(user)) != null;

            return ResponseOutput.Result(result);
        }

        public async Task<IResponseOutput> UpdateAsync(UserUpdateInput input)
        {
            if (!(input?.Id > 0))
            {
                return ResponseOutput.NotOk();
            }

            using (var uowHandle = _unitOfWorkManager.Begin<ApplicationDbContext>())
            {
                var user = _userRepository.GetAll()
                    .Where(a => a.Id == input.Id)
                    .AsNoTrackingWithIdentityResolution()
                    .FirstOrDefault();
                if (!(user?.Id > 0))
                {
                    return ResponseOutput.NotOk("用户不存在！");
                }

                Mapper.Map(input, user);
                await _userRepository.UpdateAsync(user);

                var userRoles = await uowHandle.GetActiveUnitOfWork().UserRoles.Where(a => a.UserId == user.Id)
                    .AsNoTrackingWithIdentityResolution()
                    .ToListAsync();
                userRoles.ForEach(async userRole =>
                {
                    await _userRoleBaseRepository.DeleteAsync(userRole);
                });

                if (input.RoleIds != null && input.RoleIds.Any())
                {
                    var roles = input.RoleIds.Select(a => new UserRoleEntity
                    {
                        Id = YitIdHelper.NextId(),
                        UserId = user.Id,
                        RoleId = a
                    }).ToList();

                    roles.ForEach(async role=> 
                    {
                        await uowHandle.GetActiveUnitOfWork().UserRoles.AddAsync(role);
                    });
                    
                }
                await uowHandle.SaveChangeAsync();
            }

            return ResponseOutput.Ok();
        }

        public async Task<IResponseOutput> UpdateBasicAsync(UserUpdateBasicInput input)
        {
            var entity = await _userRepository.FirstOrDefaultAsync(a => a.Id == input.Id);
            entity = Mapper.Map(input, entity);
            var result = (await _userRepository.UpdateAsync(entity)) != null;

            //清除用户缓存
            await Cache.DelAsync(string.Format(CacheKey.UserInfo, input.Id));

            return ResponseOutput.Result(result);
        }
    }
}